import { Browser, BrowserContext, chromium, Page } from 'playwright';
import { BrowserConfig } from './config/browser.config';
import { FromEntity, TestResultEntity, TestTableEntity } from './config';
import { consoleLog, isTypeOf, parseTime, splitDateString } from './utils';
import { delay, listAllAsync } from './utils/async';
import { getTargetText, unfoldArr } from './utils/data';
const os = require('os');
export default class BrowserService {
  public browser: Browser;
  public context: BrowserContext;
  public page: Page;
  public logPath: string;
  constructor() {
    const { logPath, browser, context, page } = globalThis.BrowserBasics;
    this.logPath = logPath;
    this.browser = browser;
    this.context = context;
    this.page = page;
  }

  async initBrowser() {
    this.browser = await chromium.launch(BrowserConfig.browser);
  }
  async initContext() {
    this.context = await this.browser.newContext(BrowserConfig.context);
  }
  async initPage() {
    this.page = await this.context.newPage();
  }
  async closeContext() {
    await this.context.close();
  }
  async closeBrowser() {
    await this.browser.close();
  }
  /**
   * 快照方法
   * @param file_name
   */
  async screenshot(file_name: string) {
    await delay(2000);
    await this.page.screenshot({
      path: `${this.logPath}/images/${file_name}_${parseTime(new Date().getTime(), 'YY-MM-DD HH:mm:ss')}.png`,
    });
  }
  /**
   * 提交表单方法
   * @param fromData
   */
  async submitForm(fromData: FromEntity[]) {
    const asyncFun = async (fill: FromEntity) => {
      const { type, selector, value } = fill;
      const isVisible = await this.page.isVisible(selector);
      if (isVisible) {
        switch (type) {
          case 'fill':
            await this.page.fill(selector, value);
            break;
          case 'click':
            await this.page.click(selector);
            break;
          case 'members':
            await this.setMembers(fill);
            break;
          case 'select':
            await this.setSelect(fill);
            break;
          case 'picke':
            await this.setPicke(fill);
            break;
          default:
            console.log('type error...');
        }
      } else {
        await consoleLog(`页面元素: ${selector} 不存在`, this.logPath);
      }
    };
    await listAllAsync(fromData, asyncFun);
  }
  /**
   * 点击按钮组
   * @param btnGroup
   */
  async clickGroup(btnGroup: FromEntity[]) {
    const asyncFun = async (button: FromEntity) => {
      await this.page.click(button.selector);
      await delay(button.delay || 1000);
    };
    await listAllAsync(btnGroup, asyncFun);
  }
  /**
   * 选人方法封装
   * @param fromData
   */
  async setMembers(fromData: FromEntity) {
    const { selector, value, list_selector, okBtn } = fromData;
    await this.page.click(selector); // 点击显示选人弹框
    const asyncFun = async (member: FromEntity) => {
      const ele = `${list_selector}:has-text("${member}")`;
      const isVisible = await this.page.isVisible(ele);
      if (isVisible) {
        await delay(1000);
        await this.page.click(ele); // 点击显示选人弹框
      } else {
        console.log(`${member} 不存在`);
      }
    };
    await listAllAsync(value, asyncFun);
    await this.page.click(okBtn);
  }
  /**
   * 选择列表
   * @param fromData
   */
  async setSelect(fromData: FromEntity) {
    const { selector, boxSelector, value, list_selector, search } = fromData;
    if (search) {
      const { selector, value } = search;
      await this.page.fill(selector, value);
    } else {
      await this.page.click(selector);
    }
    const isVisible = await this.page.isVisible(boxSelector);
    if (isVisible) {
      const asyncFun = async (list: FromEntity) => {
        const ele = `${list_selector}:has-text("${list}")`;
        const isVisible = await this.page.isVisible(ele);
        if (isVisible) {
          await delay(1000);
          await this.page.click(ele);
        } else {
          console.log(`${list} 不存在`);
        }
      };
      await listAllAsync(value, asyncFun);
    }
  }
  /**
   * 设置时间选择
   * @param fromData
   */
  async setPicke(fromData: FromEntity) {
    const { selector, value, boxSelector } = fromData;
    // 整理时间数组
    const date = splitDateString(value);
    await this.page.click(selector);
    const isVisible = await this.page.isVisible(boxSelector);
    if (isVisible) {
      await delay(1000);
      await this.page.click(`.thy-calendar-date:has-text("${date['DD']}")`);
    } else {
      console.log(`时间组件加载失败`);
    }
  }
  /**
   * 表单信息验证
   * @param fromData
   * @returns
   */
  async expectFormData(fromData: FromEntity[]) {
    const isVisible = await this.page.isVisible('.invalid-feedback');
    if (isVisible) {
      const errText = await this.page.innerText('form');
      // 处理错误信息
      const err_msg = fromData.filter((f) => f.err_msg).map((t) => t.err_msg);
      const _errText = getTargetText(errText, unfoldArr(err_msg));
      await consoleLog(
        `---错误提示start------${os.EOL}${_errText.join(os.EOL)}${
          os.EOL
        }------错误提示end------`,
        this.logPath
      );
      return false;
    } else {
      return true;
    }
  }
  /**
   * 验证测试是否正确
   * @param testPage
   */
  async expectTestResult(testResult: TestResultEntity[]) {
    // 批次验证结果
    const asyncFun = async (result: TestResultEntity) => {
      const { id, type, selector, value, des } = result;
      const testClass = async () => {
        // 通过字符串检索查询类
        const html = await this.page.innerHTML(selector);
        console.log(getTargetText(html, [value]).join());
        return getTargetText(html, [value]).join();
      };
      let testHtml = '';
      switch (type) {
        case 'input':
          testHtml = await this.page.inputValue(selector);
          break;
        case 'text':
          testHtml = await this.page.innerText(selector);
          break;
        case 'class':
          testHtml = await testClass();
          break;
        default:
          console.log('type error...');
      }
      const sign = testHtml === value ? '√' : '×';
      await consoleLog(`${id}: ${des} ${value} ${sign}`, this.logPath);
    };
    await listAllAsync(testResult, asyncFun);
  }
  /**
   * 表格数据验证[针对一对一方式]
   * @param table
   */
  async expectTableTestResult(tableTest: TestTableEntity) {
    // 验证前的表单操作
    const { selector, result } = tableTest;
    const isTab = await this.page.isVisible(selector);
    if (isTab) {
      const thead = await this.page.innerText(`${selector} thead`);
      const body = await this.page.innerText(
        `${selector} > tbody > tr:nth-child(1)`
      );
      const theadTextArr = thead.replace(/\n+/g, '').split(/\t/); // 去掉换行，从空格处分离
      const bodyTextArr = body.replace(/\n+/g, '').split(/\t/);
      const resultMsg = result.reduce((prev, next) => {
        const { label, value } = next;
        const index = theadTextArr.indexOf(label);
        if (index === -1) {
          prev += `${label}: 不存在`;
        } else {
          // 更加精确的话可以全等于
          const sign = bodyTextArr[index].includes(value) ? '√' : '×';
          prev += `${label}: ${bodyTextArr[index]}(${value}) ${sign}`;
        }
        return prev + os.EOL;
      }, '');
      await consoleLog(resultMsg, this.logPath);
    } else {
      console.log('列表不显示');
    }
  }
  /**
   * 输出表单配置
   * @param title
   * @param fromData
   */
  async exportConfig(title: string, fromData: FromEntity[]) {
    const labelFrom = fromData.filter((from) => from.label);
    const configText = labelFrom.map((item) => {
      const value = isTypeOf(item.value, 'Array')
        ? item.value.join(',')
        : item.value;
      return `${item.label}: ${value}`;
    });
    const _title = `------${title}------`;
    const _configText = `${_title}${os.EOL}${configText.join(os.EOL)}`;
    await consoleLog(_configText, this.logPath);
  }
}
